/**
* MythBOINC is a BOINC plugin for MythTV, it allows for BOINC Client management via the MythTV interface.
* Copyright (C) 2008  Kenneth Dixon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef MYTHBOINC_CPP
#define MYTHBOINC_CPP

/* MythTV includes */
#include <mythtv/mythcontext.h>
#include <mythtv/generictree.h>

/* Custom includes */
#include "mythboinc.h"
#include "/usr/include/pstreams/pstream.h"
#include <sstream>

using namespace std;

MythBoinc::MythBoinc(MythMainWindow *parent, QString windowName, QString themeFilename, const char *name): MythThemedDialog(parent, windowName, themeFilename, name)
{
    
    GetHosts(4);
    
}


MythBoinc::~MythBoinc(){}


string MythBoinc::IntToString(int inInt)
{
    
    stringstream ss;
    ss<<inInt;
    string outResult;
    ss>>outResult;
    
    return(outResult); 
    
}


float MythBoinc::StringToFloat(string inString)
{
    
    stringstream ss;
    ss<<inString;
    float outResult;
    ss>>outResult;
    
    return(outResult); 
    
}


string MythBoinc::FloatToString(float inFloat)
{
    
    stringstream ss;
    ss<<inFloat;
    string outResult;
    ss>>outResult;
    
    return(outResult); 
    
}


bool MythBoinc::TestClientConnection(int ID)
{
    
    bool result;
    vector<string> commandString;
    redi::ipstream in;
    redi::ipstream in2;

    //command
    in.open("which boinc_cmd");

    getline(in, cmd);
    if(!cmd.empty())
    {
        commandString.push_back(cmd);
        //in.close();
        
        //host
        commandString.push_back("--host");
        commandString.push_back(gContext->GetSetting("BoincClentIP"+IntToString(ID)));
        
        //password
        commandString.push_back("--passwd");
        commandString.push_back(gContext->GetSetting("BoincClentPwd"+IntToString(ID)));
        
        //argument
        commandString.push_back("--get_state");
        
        //execute
        in2.open("boinc_cmd", commandString, redi::pstreambuf::pstderr);
        
        allResults.clear();
        string resultLine;
        
        string errOutputLine;
        getline(in2.err(), errOutputLine);
        if(!errOutputLine.empty())
        { 
            result = false;
        }
        else
        {
            while(getline(in2.out(), resultLine))
            {
                allResults.push_back(resultLine);
                cout<<resultLine<<endl;
            }
            //VERBOSE(VB_IMPORTANT, "No connection error detected");
            result = true;
        }
    }
    else
    {
        VERBOSE(VB_IMPORTANT, "boinc_cmd does not exist!!");
        result = false;   
    }
    in.close();
    in2.close();
    
    return(result);
    
}


void MythBoinc::PopulateClientInfo(int ID)
{
    redi::ipstream in;
    redi::ipstream in2;
    vector<string> commandString;
    
    commandString.push_back(cmd);
    
    //host
    commandString.push_back("--host");
    commandString.push_back(gContext->GetSetting("BoincClentIP"+IntToString(ID)));
    
    //password
    commandString.push_back("--passwd");
    commandString.push_back(gContext->GetSetting("BoincClentPwd"+IntToString(ID)));
    
    //argument
    commandString.push_back("--get_state");
    
    //execute
    //cout<<commandString[0]<<commandString[1]<<commandString[2]<<commandString[3]<<commandString[4]<<commandString[5]<<endl;
    in.open("boinc_cmd", commandString);
    
    allResults.clear();
    string resultLine;

    while(getline(in.out(), resultLine))
    {
        allResults.push_back(resultLine);
    }
    in.close();
    
    commandString.pop_back();
    commandString.push_back("--get_host_info");
   
    in2.open("boinc_cmd", commandString);

    while(getline(in2.out(), resultLine))
    {
        allResults.push_back(resultLine);
    }    
    in2.close();
    
}


void MythBoinc::GetHosts(int numOfClients)
{
    
    root = getUIManagedTreeListType("BoincTree");
    root->showWholeTree(true);
    root->colorSelectables(true);

    BoincClientHosts = new  GenericTree("All BOINC Hosts");
    BoincClientHosts->setInt(refinedResults.size());
    refinedResults.push_back("");
    refinedResults.push_back("");
    refinedResults.push_back("");
    refinedResults.push_back("");
    refinedResults.push_back("");
    refinedResults.push_back("No Clients Attached");

    for(int i=0; i<numOfClients; i++)
    {
        if(TestClientConnection(i))
        {
            PopulateClientInfo(i);
            BoincClientHosts->addNode(AttachHost(i));   
        }   
        else
        {
            VERBOSE(VB_IMPORTANT, "Could not connect to BOINC Client: " + IntToString(i));
        }
    }
    
    root->assignTreeData(BoincClientHosts);
    
    root->setCurrentNode(BoincClientHosts->getChildAt(0,0));
    UpdateMetaData();
    
}


GenericTree* MythBoinc::AttachHost(int ID)
{
    
    GenericTree *BoincClientHost = new  GenericTree(gContext->GetSetting("BoincClentIP"+IntToString(ID)));
    BoincClientHost->setInt(refinedResults.size());
    refinedResults.push_back("Host: " + BoincClientHost->getString());

    for(unsigned i=0; i<allResults.size(); i++)
    {
        if(allResults.at(i).compare(0,12,"  timezone: ")==0)
        {
            refinedResults.push_back("CPU: " + allResults.at(i+5).substr(13, allResults.at(i+5).size()));
            refinedResults.push_back("CPU Cores: " + allResults.at(i+3).substr(9, allResults.at(i+3).size()));
            refinedResults.push_back("O/S: " + allResults.at(i+9).substr(11, allResults.at(i+9).size()) + " - " + allResults.at(i+10).substr(14, allResults.at(i).size()));
            refinedResults.push_back("");
            refinedResults.push_back("Computer Information");
        }

    }  
         
    for(unsigned i=0; i<allResults.size(); i++)
    {
        if(allResults.at(i).compare(0,15,"   master URL: ")==0)
        {
            BoincClientHost->addNode(AttachProject(allResults.at(i).substr(15, allResults.at(i).size()),i, ID));
        }
    }

    return(BoincClientHost); 
      
}


GenericTree* MythBoinc::AttachProject(string masterURL, int ptr, int ID)
{
    
    GenericTree *BoincClientProject = new  GenericTree(allResults.at(ptr-1).substr(9, allResults.at(ptr-1).size()));
    BoincClientProject->setInt(refinedResults.size());
    refinedResults.push_back("Name: " + BoincClientProject->getString());
    refinedResults.push_back("Master URL: " + allResults.at(ptr).substr(15, allResults.at(ptr).size()));
    refinedResults.push_back("User Name: " + allResults.at(ptr+1).substr(14, allResults.at(ptr+1).size()));
    refinedResults.push_back("User Credit Total: " + allResults.at(ptr+4).substr(22, allResults.at(ptr+4).size()));
    refinedResults.push_back("Host Credit Total: " + allResults.at(ptr+6).substr(21, allResults.at(ptr+6).size()));
    refinedResults.push_back("Project Infomation");
    
    for(unsigned i=0; i<allResults.size(); i++)
    {
        if(allResults.at(i).compare("   project URL: "+masterURL)==0)
        {
            BoincClientProject->addNode(AttachWorkUnit(i, ID));

        }      
    }
    
    return(BoincClientProject);
       
}


GenericTree* MythBoinc::AttachWorkUnit(int ptr, int ID)
{
    
    GenericTree *BoincClientWorkUnit = new  GenericTree(allResults.at(ptr-1).substr(12, allResults.at(ptr-1).size()));
    BoincClientWorkUnit->setInt(refinedResults.size());
    
    refinedResults.push_back("Work Unit: " + BoincClientWorkUnit->getString());
    
    if(allResults.at(ptr+10).compare("   active_task_state: 1")==0)
    {
        refinedResults.push_back("Active: Yes");
    }
    else
    {
        refinedResults.push_back("Active: No");
    }
    
    float doneNum = StringToFloat(allResults.at(ptr+15).substr(18, allResults.at(ptr+15).size()));
    refinedResults.push_back("Percent Done: " + FloatToString(doneNum*= 100.0) + "%");
    
    refinedResults.push_back("CPU Time Spent: " + allResults.at(ptr+14).substr(21, allResults.at(ptr+14).size()));
    
    if(allResults.at(ptr+19).compare("   supports graphics: yes")==0)
    {   
        if((gContext->GetSetting("BoincClentIP"+IntToString(ID)).compare("localhost")==0) || (gContext->GetSetting("BoincClentIP"+IntToString(ID)).compare("127.0.0.1")==0))
        {
            refinedResults.push_back("Graphics Supported: Yes - Select to show graphics"); 
            BoincClientWorkUnit->setSelectable(true);
        }
        else
        {
            refinedResults.push_back("Graphics Supported: Yes - Graphics unavailable for remote hosts"); 
            BoincClientWorkUnit->setSelectable(false);            
        }
    } 
    else
    {
        refinedResults.push_back("Graphics Supported: No");
        BoincClientWorkUnit->setSelectable(false);
    } 
      
    refinedResults.push_back("Work Unit Information"); 
    
    //host
    refinedResults.push_back(gContext->GetSetting("BoincClentIP"+IntToString(ID)));
    //password
    refinedResults.push_back(gContext->GetSetting("BoincClentPwd"+IntToString(ID)));
    //Master URL
    refinedResults.push_back(allResults.at(ptr).substr(16, allResults.at(ptr).size()));
    //Work Unit name
    refinedResults.push_back(allResults.at(ptr-2).substr(9, allResults.at(ptr-2).size()));

    return(BoincClientWorkUnit);
       
}


void MythBoinc::RunGraphics()
{
    
    VERBOSE(VB_IMPORTANT, "Attempting to extract graphics");
    
    AllowGraphics();
    //sleep(1);
    
    ExtractGraphics();    
    sleep(1);
     
    AdjustGraphics0();
    //sleep(1);

    AdjustGraphics1();
  
}


void MythBoinc::AllowGraphics()
{
    
    //Sets up display to allow graphics
    vector<string> commandString;
    commandString.push_back("xhost");
    commandString.push_back("+si:localuser:boinc");
    
    redi::ipstream in;
    in.open("xhost", commandString, redi::pstreambuf::pstderr);
    string resultLine;
    while(getline(in.err(), resultLine))
    {
        cout<<resultLine<<endl;
    }
    in.close(); 
      
}

void MythBoinc::ExtractGraphics()
{
    
    vector<string> commandString;
    commandString.push_back(cmd);
    commandString.push_back("--host");
    commandString.push_back(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+6));
    commandString.push_back("--passwd");
    commandString.push_back(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+7));
    commandString.push_back("--result");
    commandString.push_back(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+8));
    commandString.push_back(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+9));
    commandString.push_back("graphics_fullscreen");
    
    redi::ipstream in;
    in.open("boinc_cmd", commandString, redi::pstreambuf::pstderr);
    in.close(); 
       
}

void MythBoinc::AdjustGraphics0()
{
    
    vector<string> commandString;
    commandString.push_back("wmctrl");
    commandString.push_back("-r");
    commandString.push_back("workunit");
    commandString.push_back("-b");
    commandString.push_back("add,fullscreen");
    
    redi::ipstream in;
    in.open("wmctrl", commandString, redi::pstreambuf::pstderr);
    in.close(); 
       
}


void MythBoinc::AdjustGraphics1()
{
    
    vector<string> commandString;    
    commandString.push_back("wmctrl");
    commandString.push_back("-a");
    commandString.push_back("workunit");
    
    redi::ipstream in;
    in.open("wmctrl", commandString, redi::pstreambuf::pstderr);
    in.close();
        
}


void MythBoinc::keyPressEvent(QKeyEvent *e)
{
   
    bool handled = false;
    QStringList actions;
    
    if (gContext->GetMainWindow()->TranslateKeyPress("qt", e, actions, false))
    {
        for (unsigned i = 0; i < actions.size() && !handled; i++)
        {
            QString action = actions[i];
            handled = true;
            
            if (action == "UP")
            {
                root->moveUp();
            }
            else if (action == "DOWN")
            {
                root->moveDown();
            }
            else if (action == "LEFT")
            {
                root->popUp();
            }
            else if (action == "RIGHT")
            {
                root->pushDown();
            }
            else if (action == "SELECT")
            {
                if(getUIManagedTreeListType("BoincTree")->getCurrentNode()->isSelectable())
                {
                    RunGraphics();
                }
            }
            else
            {
                handled = false;
            }
        }
    }
    
    if (!handled)
    {
        MythThemedDialog::keyPressEvent(e);
    }
    else
    {
        UpdateMetaData();
    }

}


void MythBoinc::UpdateMetaData()
{
    
    UITextType *first = getUITextType("first");
    first->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+0));
    
    UITextType *second = getUITextType("second");
    second->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+1)); 
    
    UITextType *third = getUITextType("third");
    third->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+2)); 

    UITextType *fourth = getUITextType("fourth");
    fourth->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+3));
    
    UITextType *fifth = getUITextType("fifth");
    fifth->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+4));
    
    UITextType *infoTitle = getUITextType("infoTitle");
    infoTitle->SetText(refinedResults.at(getUIManagedTreeListType("BoincTree")->getCurrentNode()->getInt()+5));
    
}


#endif
